<?php
// +----------------------------------------------------------------------
// | LIKE [ JUST DO IT ]
// +----------------------------------------------------------------------
// | Author: Mr.hu <huhaiyang7788@163.com>
// +----------------------------------------------------------------------
// | DESC: 通用接口基类
// +----------------------------------------------------------------------


namespace common\controllers;



use api\implement\token\Mysql;
use common\behavior\EndBehavior;
use yii\filters\ContentNegotiator;
use yii\filters\VerbFilter;
use yii\rest\Controller;
use yii\web\Response;

abstract class CommonApi extends Controller
{
    use CommonBase;//横向扩展，摆脱集成，提供基础方法

    private $data_key='data';                               //定义接口传值的key

    protected static $tokenService;                         //token服务

    protected $validate_token_type='mysql';                 //用来验证token的存储方式，默认是mysql，可以是session,mongo,redis,file

    protected $validate_token_config=[];                    //关于token存储的配置，具体更具不同的实现来配置

    public $code=0;                                         //返回的错误码，默认是0

    public $error;                                          //错误消息

    public $start_time;                                     //开始执行的时间点

    public $start_memory;                                   //脚本完成初始化时的内存使用情况

    protected $key='8934e7d15453e97507ef794cf7b0519d';      //用来计算签名的key

    protected $timeout_second=60;                           //接口调用允许的最大延时秒数

    public $processed_data;                                 //经过解密验证后的数据,只能内部可见

    public $request_data;                                   //接口请求的完整数据data=>****

    public $request_datetime;                               //发起请求的时间

    public $defaultAction='index';                          //默认执行的动作


    /**
     * 被实例化优先执行
     * return void
     * @ Mr.hu.
     */
    public function init()
    {
        parent::init();
        //赋值请求参数
        $this->request_data=$this->get($this->data_key);

        //解析成明文数据并赋值
        $this->decodeData();

        //然后对数据进行检查
        $this->validateData();

        //初始化记录系统资源的使用情况
        $this->record_resource();

        //挂在token服务到$this
        self::$tokenService=$this->getTokenObj();

    }


    /**
     * 执行过程中触发的行为
     * return array
     * @ Mr.hu.
     */
    public function behaviors()
    {
        return [
            'contentNegotiator' => [
                'class' => ContentNegotiator::className(),
                'formats' => [
                    'application/json' => Response::FORMAT_JSON,
                ],
            ],
            'verbFilter' => [
                'class' => VerbFilter::className(),
                'actions' => $this->verbs(),
            ],
            'apiRecord'=>[
                'class'=>EndBehavior::className()
            ],
        ];
    }

    /**
     * 解码客户端传输的数据
     * return bool
     * @ Mr.hu.
     */
    protected function decodeData()
    {
        $base64_decode=base64_decode($this->request_data);
        if(false===$base64_decode){
            $this->code=-2;
            $this->error=$this->language(50001);
            return false;
        }
        
        $json_decode=json_decode($base64_decode,true);
        if(null===$json_decode){
            $this->code=-2;
            $this->error=$this->language(50002);
            return false;
        }

        $this->processed_data=$json_decode;
        define('REQUEST_TOKEN',$this->processed_data['token']);
        return true;
    }


    /**
     * 验证请求的有效性
     * return void
     * @ Mr.hu.
     */
    protected function validateData()
    {
        return true;
        return $this->validateRepeat() and $this->validateSign() and $this->validateTimeOut() and $this->validateVersion() and $this->validateToken();
    }

    /**
     * 记录系统资源
     * return void
     * @ Mr.hu.
     */
    protected function record_resource(){
        $this->start_time=microtime(true);
        $this->start_memory=memory_get_usage();
        $this->request_datetime=date('Y-m-d H:i:s',$_SERVER['REQUEST_TIME']);
    }


    /**
     * 错误返回
     * @param $code
     * return array
     * @ Mr.hu
     */
    protected function error($error_key){
        $this->error=$this->language($error_key);
        return ['code'=>-1,'msg'=>$error_key];
    }


    /**
     * 正确返回格式
     * @param $data 返回的数据
     * return array
     * @ Mr.hu.
     */
    protected function success($data)
    {
        return ['code'=>0,'data'=>$data];
    }

    /**
     * 验证请求的签名
     * return void
     * @ Mr.hu.
     */
    private function validateSign()
    {
        if($this->processed_data['signature']===$this->makeSign()){
            return true;
        }else{
            $this->error=$this->language(40001);
            return false;
        }
    }

    /**
     * 验证请求是否超时
     * return void
     * @ Mr.hu.
     */
    private function validateTimeOut()
    {
        //根据接口的时间参数，来判断是否超出了允许的时间范围
        return true;
    }

    /**
     * 验证接口是否重复调用
     * return void
     * @ Mr.hu.
     */
    private function validateRepeat()
    {
        //根据签名的唯一性，来判断这个请求是否已经被执行过了。这里所有的请求，都会在 mongo中记录

        return true;
    }


    /**
     * 验证接口的版本号
     * return void
     * @ Mr.hu.
     */
    private function validateVersion()
    {
            //根据参数中带的版本号和id，到数据库进行对比
            return true;
    }


    /**
     * 生成签名
     * return string
     * @ Mr.hu.
     */
    private function makeSign()
    {
        //签名步骤一：按字典序排序参数
        ksort($this->processed_data);

        //签名步骤二：拼接字符串
        $string='';
        foreach ($this->processed_data as $k => $v)
        {
            if($k != 'signature' && $v != '' && !is_array($v)){
                $string .= $k . '=' . $v . '&';
            }
        }
        $string = trim($string, "&");

        //签名步骤三：在string后加入KEY
        $string = $string . "&key=".$this->key;

        //签名步骤四：MD5加密
        $string = md5($string);

        //签名步骤五：所有字符转为大写
        $result = strtoupper($string);
        return $result;
    }

    /**
     * 子类必须实现这个方法，验证token的可行性
     * return mixed boole  true|false
     * @ Mr.hu.
     */
    abstract public function validateToken($token);


    /**
     * 获取token实例对象
     * return void
     * @ Mr.hu.
     */
    protected function getTokenObj(){
        $return_obj=null;
        switch (strtolower($this->validate_token_type)){
            case 'mysql':
                $config=$this->validate_token_config;
                $return_obj=new Mysql($config['table_name'],$config['token_filed_name'],$config['active_field_name']);
                unset($config);
                break;
        }
        return $return_obj;
    }


    /**
     * 获取解析后指定的参数值，默认获取全部
     * @param null $key
     * return mixed
     * @ Mr.hu.
     */
    protected function getProcessed($key=null)
    {
        if (is_null($key)){
            return $this->processed_data;
        }else{
            return $this->processed_data[$key];
        }
    }




}